package com.tomvian.game
{
	import flash.display.Shape;
	import flash.display.Stage;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.TimerEvent;
	import flash.system.System;
	import flash.utils.Timer;
	import flash.utils.getTimer;
	import sfb.tools.AccurateTimer;
	
	/**
	 * TickManager
	 * @author Thomas Vian
	 */
	public class TickManager
	{
		//--------------------------------------------------------------------------
		//
		//  Properties
		//
		//--------------------------------------------------------------------------
		
		private static var inst:TickManager;
		public static function get instance():TickManager { return inst ? inst : inst = new TickManager(); }
		
		public var tick:uint;
		
		private var _tickTimer:Timer;
		private var _tickAccurateTimer:AccurateTimer;
		private var _tickFrameSource:Shape;
		
		private var _tickCalls:TickCall;
		private var _tickPool:TickCall;
		private var _tickLookup:Array;
		
		private var _tickIDCount:uint;
		
		private var _tickCallsNum:uint;
		private var _tickCallsCount:uint;
		
		private var _currentTick:TickCall;
		
		private var _tickTime:uint;
		
		//--------------------------------------------------------------------------
		//
		//  Getters / Setters
		//
		//--------------------------------------------------------------------------
		
		public function set delay(value:uint):void {if (_tickTimer) _tickTimer.delay = value;}
		
		//--------------------------------------------------------------------------
		//
		//  Constructor
		//
		//--------------------------------------------------------------------------
		
		public function TickManager()
		{
			_tickPool = fillPool(200);
			_tickLookup = [];
			_tickIDCount = 1;
		}
		
		//--------------------------------------------------------------------------
		//
		//  Tick Methods
		//
		//--------------------------------------------------------------------------
		
		public function addTickCall(tickCall:Function, priority:int = 0):uint
		{
			_tickCallsNum++;
			
			if(!_tickPool) _tickPool = fillPool(50);
			
			var tick:TickCall = _tickPool;
			_tickPool = _tickPool.next;
			
			var id:uint = _tickIDCount++;
			
			_tickLookup[id] = tick;
			
			tick.tickCall = tickCall;
			tick.priority = priority;
			tick.next = null;
			tick.last = null;
			tick.id = id;
			
			if (_tickCalls)
			{
				var prioritySearch:TickCall = _tickCalls
				
				do
				{
					if (prioritySearch.priority >= priority) 
					{
						tick.last = prioritySearch.last;
						tick.next = prioritySearch;
						
						if(prioritySearch.last) prioritySearch.last.next = tick;
						prioritySearch.last = tick;
						
						if (prioritySearch == _tickCalls) _tickCalls = tick;
						
						break;
					}
					
					if (!prioritySearch.next)
					{
						prioritySearch.next = tick;
						tick.last = prioritySearch;
						
						break;
					}
					
					prioritySearch = prioritySearch.next;
				}
				while (prioritySearch)
				
			}
			else 
			{
				_tickCalls = tick;
			}
			
			return id;
		}
		
		public function removeTickCall(id:uint):void
		{
			var tick:TickCall = _tickLookup[id];
			
			if (tick)
			{
				_tickCallsNum -= 1;
				_tickCallsCount -= 1;
				
				if (_currentTick == tick) _currentTick = tick.last;
				
				_tickLookup[id] = null;
				
				tick.tickCall = null;
				tick.priority = 0;
				tick.id = 0;
				
				if(tick.last) tick.last.next = tick.next;
				if(tick.next) tick.next.last = tick.last;
				tick.last = null;
				
				tick.next = _tickPool;
				_tickPool = tick;
			}
		}
		
		public function removeAll():void
		{
			while (_tickCalls)
			{
				_tickLookup[_tickCalls.id] = null;
				
				_tickCalls.tickCall = null;
				_tickCalls.priority = 0;
				_tickCalls.id = 0;
				
				_tickCalls.last = null;
				
				var next:TickCall = _tickCalls.next;
				
				_tickCalls.next = _tickPool;
				_tickPool = _tickCalls;
				
				_tickCalls = next;
			}
		}
		
		//--------------------------------------------------------------------------
		//
		//  Start / Stop Methods
		//
		//--------------------------------------------------------------------------
		
		public function startTimer(delay:uint = 40):void
		{
			stop();
			
			tick = 0;
			
			if (!_tickTimer)
			{
				_tickTimer = new Timer(delay);
				_tickTimer.addEventListener(TimerEvent.TIMER, timerTick);
			}
			
			_tickTimer.delay = delay;
			_tickTimer.start();
		}
		
		public function startAccurateTimer(delay:uint = 40):void
		{
			stop();
			
			tick = 0;
			
			if (!_tickTimer)
			{
				_tickAccurateTimer = new AccurateTimer(delay);
				_tickAccurateTimer.addEventListener(TimerEvent.TIMER, timerTick);
			}
			
			_tickAccurateTimer.delay = delay;
			_tickAccurateTimer.start();
		}
		
		
		public function startFrame():void
		{
			stop();
			
			tick = 0;
			_total = 0;
			
			if (!_tickFrameSource)
			{
				_tickFrameSource = new Shape();
			}
			
			_tickFrameSource.addEventListener(Event.ENTER_FRAME, frameTick);
		}
		
		public function stop():void
		{
			if (_tickTimer) 		_tickTimer.stop();
			if (_tickAccurateTimer) _tickAccurateTimer.stop();
			if (_tickFrameSource) 	_tickFrameSource.removeEventListener(Event.ENTER_FRAME, frameTick);
		}
		
		private function timerTick(e:TimerEvent):void
		{
			tick++;
			
			_currentTick = _tickCalls;
			
			while (_currentTick)
			{
				_currentTick.tickCall();
				_currentTick = _currentTick.next;
			}
			
			e.updateAfterEvent();
		}
		
		protected var _total:uint;
		
		private function frameTick(e:Event):void
		{
			tick++;
			
			_currentTick = _tickCalls;
			
			while (_currentTick)
			{
				_currentTick.tickCall();
				_currentTick = _currentTick.next;
			}
		}
		
		//--------------------------------------------------------------------------
		//
		//  Pool Methods
		//
		//--------------------------------------------------------------------------
		
		private function fillPool(num:uint):TickCall
		{
			
			//var pool:TickCall = tick;
			
			/*for (var i:uint = 1; i < num; ++i)
			{
				tick.next = new TickCall();
				tick = tick.next;
			}*/
			
			return new TickCall();
		}
	}
}

/**
 * TickCall
 * @author Thomas Vian
 */
class TickCall
{
	//--------------------------------------------------------------------------
	//
	//  Properties
	//
	//--------------------------------------------------------------------------
	
	public var tickCall:Function;
	public var priority:int;
	public var id:uint;
	
	public var last:TickCall;
	public var next:TickCall;
	
	public function TickCall()
	{
		
	}
}